Lambda 是 Modern C++ 的主要功能之一,對於 C++ 程式碼的改善有決定性的影響。想要掌握 Modern C++,把 Lambda 學好、弄熟是必要條件。上一篇簡單介紹 Lambda,這一篇把重點放在 Capture Clause,也就是下圖的前端,藍色區塊:
Lambda 使得好,Capture 很重要。理解 Capture Clause 需要對 C++ Scope 有正確的認知,建議先複習。
底下程式碼展示未使用 Lambda 叫用 STL Algorithm 的一種寫法:
bool AdmitTaiwanIsPartOfChina(const std::string& name)
{
if (name == "One Smell" || name == "COCO" || name == "Milkshake")
return true;
return false;
}
std::vector<TeaShopOwner> owners = {"One Smell", "COCO", "Fifty", "Milkshake"};
std::vector<TeaShopOwner> shitters;
std::copy_if(begin(owners), end(owners),
std::back_inserter(shitters),
AdmitTaiwanIsPartOfChina);
如同「手搖飲」公開承認台灣屬中國一部分,有些人覺得沒什麼問題,有些人覺得大有問題。 上述程式碼乍看之下好像沒什麼問題,但使用 AdmitTaiwanIsPartOfChina
這類 Predicate 有一些限制,諸如缺少內部狀態、參數限制為一個(不考慮 Binders)。
上述程式碼可用 Lambda 改寫:
std::vector<TeaShopOwner> owners = {"One Smell", "COCO", "Fifty", "Milkshake"};
std::vector<TeaShopOwner> shitters;
std::copy_if(begin(owners), end(owners),
std::back_inserter(shitters),
[](const std::string& name)
{
if (name == "One Smell" || name == "COCO" || name == "Milkshake")
return true;
return false;
});
上述版本的「好處」之一是,std::copy_if
的 Predicate 邏輯直接寫在呼叫處。接下來,假設 std::copy_if
Predicate 內部的邏輯需要來自外部的資料,此時,便可使用 Capture 來「抓取」。為簡化程式碼,底下用其他範例來展示。
C++ Lambda Capture 有幾種模式:
寫法上,又分兩種:
Capture-default by-value 用一個 =
符號表示:
std::vector<int> sources = {1, 2, 3, 4, 5, 6, 7, 8};
int minimal = 6;
auto count = std::count_if(begin(sources), end(sources),
[=](int s)
{
return s > minimal;
});
上例中,sources
以及 minimal
皆被「抓取」至 Lambda 中成為可見,而且是以 By-value 的形式抓取,意思是會產生一個 minimal
的複製品供 Lambda 使用,作用於 Lambda 裡的 minimal
不會影響外部那個。此寫法即為 capture-default
。不過,由於 Lambda 內部只用到了 minimal
,因此,比較好的寫法是 individual capture
:
std::vector<int> sources = {1, 2, 3, 4, 5, 6, 7, 8};
int minimal = 6;
auto count = std::count_if(begin(sources), end(sources),
[minimal](int s)
{
return s > minimal;
});
By-value 的使用要特別留意「物件複製的成本」,成本太高而且該片段會被頻繁使用的話,可以利用 By-reference 的方式傳遞。下一篇就來談 Capture by-reference。